package io.lvdaxian.mybatis05.binding;


import cn.hutool.core.lang.ClassScanner;
import io.lvdaxian.mybatis05.session.Configuration;
import io.lvdaxian.mybatis05.session.SqlSession;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapperRegistry {
  // 注册 Class 以及 ClassProxyFactory 代理类
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
  private final Configuration configuration;
  
  public MapperRegistry(Configuration configuration) {
    this.configuration = configuration;
  }
  
  /**
   * 根据调用接口 class，以及 SqlSession 查询proxy类
   *
   * @param clazz      要代理的接口
   * @param sqlSession sql session
   * @param <T>        动态泛型类型
   * @return 返回代理类
   * @author lihh
   */
  public <T> T getMapper(Class<T> clazz, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(clazz);
    
    // 判断 Class 对应的代理工厂 是否存在
    if (null == mapperProxyFactory)
      throw new RuntimeException("Type " + clazz + " is not known to the MapperRegistry.");
    
    return mapperProxyFactory.newInstance(sqlSession);
  }
  
  /**
   * 判断 mapper key 是否存在
   *
   * @param clazz 将要添加的接口类
   * @return 返回是否存在状态
   * @author lihh
   */
  public <T> boolean hasMapper(Class<T> clazz) {
    return knownMappers.containsKey(clazz);
  }
  
  /**
   * 根据 Class 添加 Mapper，根据 clazz 以及 MapperProxyFactory 生成实例
   *
   * @param clazz 接口类
   * @author lihh
   */
  public <T> void addMapper(Class<T> clazz) {
    // 必须是接口
    if (!clazz.isInterface()) return;
    
    // mapper 是否存在
    if (hasMapper(clazz))
      // 如果重复添加了，报错
      throw new RuntimeException("Type " + clazz + " is already known to the MapperRegistry.");
    
    knownMappers.put(clazz, new MapperProxyFactory<>(clazz));
  }
  
  /**
   * 添加 classPath mapper 的逻辑
   *
   * @param basePackage package 基础逻辑
   * @author lihh
   */
  public void addMappers(String basePackage) {
    Set<Class<?>> classes = ClassScanner.scanPackage(basePackage);
    
    for (Class<?> clazz : classes)
      addMapper(clazz);
  }
}
